package org.msi.bayesiannetwork;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>Represents a conditional probability.</p>
 *
 * @author Jakub Narloch
 * @version 1.0
 */
public class ConditionalProbability {

    /**
     * <p>Represents the list of variable names.</p>
     */
    private List<String> variableNames;

    /**
     * <p>Represents the map of conditional probabilities.</p>
     */
    private Map<BooleanSequence, Double> probabilities;

    /**
     * <p>Creates new instance of {@link ConditionalProbability} class with specified variable names and
     * probabilities.</p>
     *
     * @param variableNames   the variable names
     * @param probabilityList the probability list
     */
    public ConditionalProbability(List<String> variableNames, List<Double> probabilityList) {

        // TODO add input validation - need to check if probabilityList has 2 ^ (variables.size) elements
        this.variableNames = variableNames;

        int variableCount = variableNames.size();
        int index = 0;
        probabilities = new HashMap<BooleanSequence, Double>();
        for (Double probability : probabilityList) {

            probabilities.put(new BooleanSequence(Utils.intToBooleanList(index, variableCount)), probability);
            index++;
        }
    }

    /**
     * <p>Retrieves the probability.</p>
     *
     * @param sequence the variable sequqnce
     *
     * @return the probability
     */
    public double getProbability(BooleanSequence sequence) {

        return probabilities.get(sequence);
    }

    /**
     * <p>Retrieves the probability.</p>
     *
     * @param variablesMap the variable map, where as keys are used variable names and as values the variable 'value'
     *
     * @return the probability
     */
    public double getProbability(Map<String, Boolean> variablesMap) {

        // TODO check input
        if (variablesMap.size() != getVariableCount()) {

            throw new IllegalArgumentException("Parameter 'variablesMap' doesn't contain all variables," +
                    "or the number of the variables exceed the actual size.");
        }

        for (String variableName : variableNames) {

            if (!variablesMap.containsKey(variableName)) {
                throw new IllegalArgumentException(
                        String.format("Parameter 'variablesMap' doesn't contain %s variable.", variableName));

            }

            if (variablesMap.get(variableName) == null) {
                throw new IllegalArgumentException("Parameter 'variablesMap' can not contain null values.");
            }
        }

        List<Boolean> booleanSequence = new ArrayList<Boolean>();

        for (String variableName : variableNames) {

            booleanSequence.add(variablesMap.get(variableName));
        }

        return getProbability(new BooleanSequence(booleanSequence));
    }

    /**
     * <p>Retrieves the variable count.</p>
     *
     * @return the variable count
     */
    public int getVariableCount() {

        return variableNames.size();
    }

    /**
     * <p>Retrieves the variable names.</p>
     *
     * @return the variable names
     */
    public List<String> getVariableNames() {

        // makes the  shallow copy of the variableNames
        return new ArrayList<String>(variableNames);
    }
}
